
昨天看了俠客 Google Cloud Speech-to-Text 的實測身手,
今天回到 App,看看錄音怎麼送上山、文字怎麼帶回來。
請俠客聽寫,有三種 委託方式:
| 方式 | 什麼時候送 | 什麼時候拿到文字 | 適合 |
|---|---|---|---|
| 同步辨識 | 錄完,整段一次送出 | 送出之後,一次拿到完整結果 | 1 分鐘內的短錄音 |
| 串流辨識 | 一邊錄,一邊送 | 說話的同時,陸續拿到 | 即時、邊說邊辨識 |
| 批次辨識 | 錄音先放到 Cloud Storage | 處理完成後,再取回結果 | 1 分鐘以上的長錄音 |
健口操和小遊戲的錄音都很短,這次只用到同步、串流兩種。
這兩種方式,各有要解決的問題:
錄好的一段話,要怎麼交給鏢局、請俠客寫成文字?邊說邊出,鏢局一趟一趟押鏢,還來得及嗎?`今天實作兩種辨識接進 App,讓我們往下看!
| 站 | 做什麼 |
|---|---|
| App | 錄好 WAV,轉成 Base64,呼叫函式 recognizeExercise |
| 鏢局(Cloud Functions) | 用服務帳戶換令牌,把錄音交給 STT |
| 俠客(Cloud STT) | 用 Chirp 3 辨識,回傳文字 |
Day15 提過:呼叫 Google 自家的服務,
函式可以用自己的身分(服務帳戶)呼叫,連金鑰都不需要。令牌由鏢局在執行時換來,效期很短;App 從頭到尾不碰任何金鑰。
Google Cloud 的服務預設是關閉的,要先在專案裡啟用。
Firebase 專案本身就是一個 Google Cloud 專案,
到 Google Cloud 主控台選同一個專案就好。
① 開啟 Google Cloud 主控台,左上角選擇 Firebase 的專案

② 「API 與服務」→「程式庫」,搜尋 Cloud Speech-to-Text API


③ 按「啟用」

函式呼叫 STT 時,Google 會先確認:
「是誰在呼叫」、「它有沒有這個權限」。
自己操作 Google Cloud 時,用的是自己的 Google 帳號;
函式在雲端執行時沒有人登入,
所以 Google 會給它一個「機器用的帳號」,叫服務帳戶(service account)。
函式沒特別指定時,用的是專案的 Compute Engine 預設服務帳戶:
〈專案編號〉-compute@developer.gserviceaccount.com
① Google Cloud 主控台 →「IAM 與管理」→「IAM」

② 找到這個服務帳戶,看「角色」欄位

這次的專案裡,它已經有「編輯者」角色,包含 STT 的權限,不用另外設定。
如果沒有(例如公司或學校帳號的專案),按上方的「授予存取權」:
再按「儲存」。
小提醒:
「編輯者」的權限,比呼叫 STT 需要的多很多;
Google Cloud 官方建議 換成權限較小的角色。
串流辨識(下一章節),會示範建立只有 STT 權限的專用帳戶。
這支函式用的是 callable(onCall)。
它是 Cloud Functions 專門給「自家 App」呼叫的方式,
詳見:Day14「三、(一)先決定函式類型:onRequest 還是 onCall」
在 functions/index.js 加上 recognizeExercise,
onCall、HttpsError、logger 沿用 Day14 引入的:
const {applicationDefault} = require("firebase-admin/app");
// Chirp 3 放在 us 多區域,網址的區域要跟模型一致
const location = "us";
const recognizer = `projects/${process.env.GCLOUD_PROJECT}` +
`/locations/${location}/recognizers/_`;
const sttUrl = `https://${location}-speech.googleapis.com/v2/` +
`${recognizer}:recognize`;
exports.recognizeExercise = onCall(
{enforceAppCheck: true},
async (request) => {
const audioBase64 = request.data?.audioBase64;
if (typeof audioBase64 !== "string" || !audioBase64) {
throw new HttpsError("invalid-argument", "缺少錄音");
}
// 用函式的服務帳戶,換一張短效期的令牌(存取權杖)
const {access_token: token} =
await applicationDefault().getAccessToken();
const response = await fetch(sttUrl, {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
config: {
// WAV 有檔頭,讓 STT 自己讀取樣率、聲道
autoDecodingConfig: {},
model: "chirp_3",
languageCodes: ["cmn-Hant-TW"],
},
content: audioBase64,
}),
});
if (!response.ok) {
// 原因記在 log,App 收到明確的錯誤代碼
logger.error("STT 呼叫失敗", {status: response.status});
throw new HttpsError("unavailable", "辨識暫時無法完成");
}
const result = await response.json();
// 每一段結果取第一個候選,接成完整的文字
const transcript = (result.results ?? [])
.map((r) => r.alternatives?.[0]?.transcript ?? "")
.join("");
logger.info("recognizeExercise 完成");
return {transcript};
},
);
幾個重點寫法:
| 寫法 | 說明 |
|---|---|
| applicationDefault().getAccessToken() | 用函式的服務帳戶換令牌,程式碼裡沒有金鑰 |
| us-speech.googleapis.com、locations/us | Chirp 3 在 us 多區域,網址和路徑的區域要一致 |
| recognizers/_ | 不另外建立辨識器,設定直接放在請求裡 |
| autoDecodingConfig | WAV 有檔頭,讓 STT 自己讀格式 |
| content | 錄音的 Base64 |
firebase deploy --only functions:recognizeExercise

錄音格式:16 kHz、單聲道、16-bit 的 WAV。
原生端錄音(iOS 的 AVAudioEngine、Android 的 AudioRecord),
再透過 MethodChannel 交給 Flutter;這部分篇幅較長,就不開展。
錄好之後,轉成 Base64 呼叫函式:
final functions =
FirebaseFunctions.instanceFor(region: 'asia-east1');
final callable = functions.httpsCallable('recognizeExercise');
// wavBytes:錄好的 16 kHz、單聲道 WAV
final response = await callable.call({
'audioBase64': base64Encode(wavBytes),
});
final transcript = response.data['transcript'] as String;
callable 的 SDK 會自動附上 App Check 通行證,不需要另外處理。
用 App 和 curl 各呼叫一次:
| 呼叫方式 | 結果 |
|---|---|
| App 送出錄音 | 收到辨識文字 |
| curl,沒有通行證 | 401 UNAUTHENTICATED |
Google Cloud 主控台的 Logs Explorer,也看得到每次呼叫:

以下是 2026-09-30 的實測,錄音是我自己念的繞口令(11.8 秒):
四是四,十是十,十四是十四,四十是四十。
① 俠客交給鏢局的原始回應(STT → 函式):
{
"results": [
{
"alternatives": [
{"transcript": "4是4,10是10,14是14,40是40。"}
],
"languageCode": "cmn-Hant-TW"
}
],
"metadata": {
"totalBilledDuration": "12s",
"requestId": "6c8e8a2d-……",
"prompt": "Transcribe the following speech segment ……"
}
}
欄位說明(Recognize V2):
| 欄位 | 說明 |
|---|---|
| results | 依錄音順序排列的辨識結果,可能不只一段 |
| alternatives | 候選文字,第一個是最可能的 |
| totalBilledDuration | 計費秒數:11.8 秒的錄音,算 12 秒 |
| prompt | Chirp 3 轉錄時用的指令 |
prompt 裡有一條「When transcribing numbers, write the digits」:
數字要寫成阿拉伯數字。
Day16 看到 Chirp 3 把「十四」寫成「14」,跟這條指令一致。
② App 拿到的回應(函式 → App):
{"transcript": "4是4,10是10,14是14,40是40。"}
函式只挑出文字交給 App;
計費秒數、prompt 都留在Firebase Functions,不會傳到裝置端。
同步辨識用的 callable,是「一問一答」:
錄完一整段送出,等鏢局回覆一次,就結束了。串流辨識則像打電話:
線路要一直開著,雙方隨時都能說話。
callable 做不到這件事,
所以這次在山腳下,另外開一間驛站:Cloud Run 上的 WebSocket 服務。
| 站 | 做什麼 |
|---|---|
| App | 每 100 ms 送一段錄音(PCM) |
| 驛站(Cloud Run) | 驗證通行證,把聲音轉給 STT、把文字轉回 App |
| 俠客(Cloud STT) | 用 Chirp 3 串流辨識,陸續回傳文字 |
App 和驛站之間用 WebSocket,驛站和俠客之間用 gRPC。話一段一段傳上山,文字一段一段帶回來。
俠客回傳的文字,分成兩種:
Cloud Run 支援 WebSocket,不需要額外設定(官方說明)。
但是,Cloud Run 要在程式裡,自己驗證通行證。
① Google Cloud 主控台 →「IAM 與管理」→「服務帳戶」→「建立服務帳戶」

② 名稱填 kenkou-stt-stream

③ 角色選「Cloud Speech 用戶端」(roles/speech.client),按「完成」

建立 streaming 資料夾,安裝三個套件:
mkdir streaming && cd streaming
npm init -y
npm install ws firebase-admin @google-cloud/speech
再加上 Dockerfile,告訴 Cloud Run 怎麼建置、啟動:
FROM node:24-bookworm-slim
WORKDIR /service
COPY package.json package-lock.json ./
RUN npm ci --omit=dev
COPY index.js ./
USER node
CMD ["node", "index.js"]
驛站程式 index.js 分成四段:
① 準備:讀設定、建立 STT 用戶端
const http = require("node:http");
const {WebSocketServer} = require("ws");
const {initializeApp} = require("firebase-admin/app");
const {getAppCheck} = require("firebase-admin/app-check");
const {v2} = require("@google-cloud/speech");
const projectId = process.env.GOOGLE_CLOUD_PROJECT;
// 只放行自家 App:iOS、Android 的 App ID,用逗號隔開
const allowedAppIds = process.env.ALLOWED_APP_IDS.split(",");
initializeApp({projectId});
// 在 Cloud Run 上,會自動用服務帳戶的身分呼叫 STT
const speech = new v2.SpeechClient({
apiEndpoint: "us-speech.googleapis.com",
});
const server = http.createServer();
const wss = new WebSocketServer({noServer: true});
const send = (ws, message) => ws.send(JSON.stringify(message));
② 守衛:升級成 WebSocket 之前,先驗證通行證
server.on("upgrade", async (req, socket, head) => {
try {
const token = req.headers["x-firebase-appcheck"];
if (req.url !== "/stt" || !token) throw new Error();
const claims = await getAppCheck().verifyToken(token);
if (!allowedAppIds.includes(claims.appId)) throw new Error();
} catch (_) {
socket.end("HTTP/1.1 401 Unauthorized\r\n\r\n");
return;
}
wss.handleUpgrade(req, socket, head,
(ws) => wss.emit("connection", ws));
});
驗證方式照官方的自建後端驗證 App Check:
從 X-Firebase-AppCheck 標頭取出通行證,交給 verifyToken() 驗證。
這裡多檢查一步 App ID,只放行自家的 iOS、Android App。
③ 開一條 STT 串流:第一則訊息放辨識設定
function openSpeechStream() {
const stream = speech._streamingRecognize();
stream.write({
recognizer: `projects/${projectId}/locations/us/recognizers/_`,
streamingConfig: {
config: {
model: "chirp_3",
languageCodes: ["cmn-Hant-TW"],
// 串流送的是沒有檔頭的 PCM,要明講格式
explicitDecodingConfig: {
encoding: "LINEAR16",
sampleRateHertz: 16000,
audioChannelCount: 1,
},
},
// 還沒定案的暫時結果,也要回傳
streamingFeatures: {interimResults: true},
},
});
return stream;
}
小提醒:為什麼用 _streamingRecognize()?
套件的 streamingRecognize(),是照 V1 的格式包資料;
V2 要的欄位不一樣(recognizer、audio),所以改用底層的方法,每則訊息自己包。
④ 轉送:聲音往上送、文字往回送
// 定案結果各送一則;暫時結果可能一次好幾段,接起來送一則
function forwardResults(ws, response) {
let interim = "";
for (const result of response.results ?? []) {
const text = result.alternatives?.[0]?.transcript ?? "";
if (result.isFinal) {
send(ws, {type: "result", transcript: text, isFinal: true});
} else {
interim += text;
}
}
if (interim) {
send(ws, {type: "result", transcript: interim, isFinal: false});
}
}
wss.on("connection", (ws) => {
let stt;
ws.on("message", (data, isBinary) => {
if (isBinary) {
// 錄音片段:原樣轉給 STT
stt?.write({audio: data});
return;
}
let message;
try {
message = JSON.parse(data.toString());
} catch (_) {
return ws.close();
}
if (message.type === "start" && !stt) {
stt = openSpeechStream();
stt.on("data", (response) => forwardResults(ws, response));
stt.on("error", () => {
send(ws, {type: "error"});
ws.close();
});
// STT 回完最後的結果,這次辨識就結束
stt.on("end", () => {
send(ws, {type: "done"});
ws.close();
});
send(ws, {type: "ready"});
} else if (message.type === "stop") {
// 聲音送完了:告訴 STT 不會再有新的聲音
stt?.end();
}
});
// App 中途斷線:一併關掉 STT 串流
ws.on("close", () => stt?.cancel());
});
server.listen(process.env.PORT || 8080);
小提醒:
只要還有 WebSocket 連線開著,Cloud Run 就算在使用中、持續計費
(官方說明)。所以示範專案的驛站,還加了幾道限制:
錄音最多 3 分鐘、閒置 10 秒就斷線、同時最多 2 條連線。避免連線一直開著,費用跟著累積。
Firebase CLI 部署的是 Firebase 的服務(例如 Functions);
一般的 Cloud Run 服務,要用 Google Cloud CLI(gcloud)部署。
不想在本機安裝的話,可以用 Google Cloud 主控台內建的 Cloud Shell,
裡面已經裝好 gcloud。
① 在本機把 streaming 資料夾壓縮成 zip,
不含 node_modules(建置時會重新安裝):
zip -r streaming.zip streaming -x "streaming/node_modules/*"
② 在 Google Cloud 主控台右上角,開啟 Cloud Shell,
從工具列的「⋮」→「上傳」,上傳 streaming.zip



③ 在 Cloud Shell 解壓縮,進到資料夾:
unzip streaming.zip && cd streaming
④ 在資料夾裡建立 env.yaml,放驛站要用的設定:
GOOGLE_CLOUD_PROJECT: kenkou-tw-dev
ALLOWED_APP_IDS: "iOS 的 App ID,Android 的 App ID"
App ID 在 Firebase 主控台 →「專案設定」→「一般」→「你的應用程式」:

⑤ 部署:
SA_EMAIL=kenkou-stt-stream@kenkou-tw-dev.iam.gserviceaccount.com
gcloud run deploy kenkou-stt-stream \
--source . \
--region asia-east1 \
--service-account "$SA_EMAIL" \
--env-vars-file env.yaml \
--no-invoker-iam-check \
--timeout 240 \
--max-instances 1
第一次部署,會詢問是否啟用需要的 API、建立存放映像檔的儲存庫,
都回答 y 就好。
| 參數 | 意思 |
|---|---|
| --source . | 用目前資料夾的 Dockerfile 建置映像檔,再部署 |
| --region asia-east1 | 跟函式一樣放在台灣 |
| --service-account | 用步驟 1 建立的專用帳戶 |
| --env-vars-file | 專案 ID、放行的 App ID |
| --no-invoker-iam-check | 不檢查 Google Cloud 的帳號身分,手機才連得進來 |
| --timeout 240 | 一條連線最長 240 秒(預設 5 分鐘) |
| --max-instances 1 | 最多 1 個執行個體,示範用,控制費用 |
小提醒:--no-invoker-iam-check
Cloud Run 預設只讓有 Google Cloud 權限的帳號呼叫,
手機上的 App 沒有這種身分,所以要關掉這道檢查。關掉之後,
任何人都敲得到驛站的門,步驟 2 的守衛一定要在。
部署完成後,在 Cloud Run 主控台點進服務,
「安全性」分頁會顯示「允許公開存取」(官方說明):

驛站的網址:把服務網址的 https 換成 wss,後面加上 /stt。
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:firebase_app_check/firebase_app_check.dart';
const streamUrl = 'wss://你的服務網址/stt';
Future<WebSocket> startStreaming(
void Function(String text) onText,
) async {
// ① 帶著通行證連上驛站
final token = await FirebaseAppCheck.instance.getToken();
final socket = await WebSocket.connect(
streamUrl,
headers: {'X-Firebase-AppCheck': token!},
);
// ② 暫時結果「替換」,定案結果「接在後面」
final ready = Completer<void>();
var finalText = '';
socket.listen((message) {
final data = jsonDecode(message as String);
switch (data['type']) {
case 'ready':
ready.complete();
case 'result':
final text = data['transcript'] as String;
if (data['isFinal'] == true) {
finalText += text;
onText(finalText);
} else {
onText(finalText + text);
}
}
});
// ③ 通知驛站開始,收到 ready 再送錄音
socket.add(jsonEncode({'type': 'start'}));
await ready.future;
return socket;
}
錄音時,每收到一段 PCM 就送出;說完了,通知驛站:
// 錄音中:每段約 100 ms
socket.add(pcmChunk);
// 說完了:等最後的結果回來
socket.add(jsonEncode({'type': 'stop'}));
錄音格式跟同步辨識一樣:16 kHz、單聲道、16-bit,
但不包 WAV 檔頭,直接送 PCM。官方建議每段約 100 ms,
在延遲和效率之間取得平衡(官方說明)。callable 的 SDK 會自動附上通行證;
自己開的 WebSocket,要用 getToken() 取得,放進標頭。
用 curl 和 App 分別連線:
| 連線方式 | 結果 |
|---|---|
| curl,沒有通行證 | 401 Unauthorized |
| curl,帶一張假的通行證 | 401 Unauthorized |
| App(通過 App Check) | 連線成功,說話時陸續出現文字 |
用 curl 測試時,要帶上 WebSocket 升級的標頭:
curl -i --http1.1 https://你的服務網址/stt \
-H "Connection: Upgrade" \
-H "Upgrade: websocket" \
-H "Sec-WebSocket-Version: 13" \
-H "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ=="
實測影片:
https://youtube.com/shorts/u71r7vspqrc?feature=share
每個模型只在特定區域提供,
網址、路徑、模型三個地方的區域要一致:
| 模型 | 區域 | 網址 |
|---|---|---|
| chirp_3 | us | us-speech.googleapis.com |
| chirp_2 | asia-southeast1 | asia-southeast1-speech.googleapis.com |
// 換模型時,區域跟著一起換
const PROVIDERS = {
chirp_2: {model: "chirp_2", location: "asia-southeast1"},
chirp_3: {model: "chirp_3", location: "us"},
};
const {model, location} = PROVIDERS[provider];
Chirp 3 正式提供的區域是 us、eu 兩個多區域;
Chirp 2 在 asia-southeast1、us-central1、europe-west4。
Day16 實測的語音調整,寫法是這樣:
config: {
// ……model、languageCodes 跟前面一樣
adaptation: {
phraseSets: [{
inlinePhraseSet: {
// boost 可以不設;要設的話範圍 0~20
boost: 5,
phrases: ["怕", "踏", "卡", "啦"].map((value) => ({value})),
},
}],
},
},
| 寫法 | 說明 |
|---|---|
| phraseSets | 提示詞清單,可以放好幾組 |
| inlinePhraseSet | 提示詞直接寫在請求裡,不用先另外建立 |
| boost | 加權值,越大越容易辨識成提示詞(官方說明) |
效果在 Day16 實測過:
加入提示詞後,部分字形更符合預期;提示詞也可能讓沒說出口的字被辨識出來,
開了之後,要用實際錄音比較看看。
串流多了 streamingFeatures,可以調整結果怎麼回傳:
streamingFeatures: {
// 還沒定案的暫時結果,也要回傳
interimResults: true,
// 偵測到開始說話、停止說話時,另外通知
enableVoiceActivityEvents: true,
// 停頓多久,算是一句說完
endpointingSensitivity: "ENDPOINTING_SENSITIVITY_STANDARD",
},
endpointingSensitivity 有三種(Chirp 3):
| 設定 | 適合 | 特色 |
|---|---|---|
| STANDARD(預設) | 長句、一般對話 | 延遲和準確度取得平衡 |
| SHORT | 一句話、指令 | 比預設更快回應 |
| SUPERSHORT | 很短的指令、單字 | 延遲最低,一停下來就定案 |
健口操的「怕、踏、卡、啦」都是單音,看起來適合 SUPERSHORT;
不過官方建議,只在速度很重要、而且話很短的情況使用。官方也提醒:反應調得越快,說話中間稍微停頓,就可能被提早截斷。
長輩說話的停頓通常比較多,這點要特別留意。
enableVoiceActivityEvents 打開後,
回應會多出「開始說話」「停止說話」的事件(官方說明);
前面的驛站程式沒有處理這些事件,用不到可以不開。
小提醒:想分辨「誰在說話」?
Chirp 3 的說話者分段標記 Speaker Diarization:不支援串流辨識,中文只支援簡體,不支援台灣繁中。
這一套跑起來,會花錢的有三站:
| 站 | 服務 | 怎麼計費 |
|---|---|---|
| 俠客 | Cloud STT | 錄音長度 |
| 驛站 | Cloud Run | 連線開著的時間 |
| 鏢局 | Cloud Functions | 呼叫次數、執行時間 |
V2 的標準模型,每個帳單帳戶每月前 50 萬分鐘,每分鐘 US$0.016(官方價格):
健口操和小遊戲,都會用到語音辨識。
假設每位使用者每天用 5~10 分鐘:
| 情境 | 錄音長度 | 費用 |
|---|---|---|
| 念一次繞口令 | 11.8 秒(算 12 秒) | US$0.0032 |
| 一位使用者,一個月 | 150~300 分鐘 | US$2.4~4.8 |
| 100 位使用者,一個月 | 1.5 萬~3 萬分鐘 | US$240~480 |
串流也是照送進去的錄音長度計算,沒在說話的空檔也算在內:
小遊戲一場 60 秒,串流就開 60 秒。健口操的練習時間比較好估;
小遊戲玩幾場,就看使用者有多喜歡。玩得越多,費用也跟著增加。
Cloud Run 預設只在處理請求時計費;
WebSocket 連線開著,就算是在處理請求。
台灣(asia-east1)的價格與每月免費額度(官方價格):
| 項目 | 單價 | 每月免費額度 |
|---|---|---|
| CPU | US$0.000024/vCPU 秒 | 18 萬 vCPU 秒 |
| 記憶體 | US$0.0000025/GiB 秒 | 36 萬 GiB 秒 |
| 請求 | US$0.40/百萬次 | 200 萬次 |
合計約 US$0.0015,大約是同一分鐘 STT 費用的十分之一。
每月免費的 18 萬 vCPU 秒,換算成一條連線,大約是 3,000 分鐘;
免費額度是整個帳單帳戶共用,每個月重新計算。
Blaze 方案的 Cloud Functions,每月免費額度(Firebase 價格):
| 項目 | 每月免費額度 |
|---|---|
| 呼叫次數 | 200 萬次 |
| 記憶體 | 40 萬 GB 秒 |
| CPU | 20 萬 CPU 秒 |
| 對外網路 | 5 GB |
超過之後,照 Google Cloud 的價格計算。
| 方法 | 請求次數 | 用途 |
|---|---|---|
| v2 Recognize | 716 次 | 同步辨識(Day16、Day17 實測) |
| v2 StreamingRecognize | 23 次 | 串流辨識(小遊戲、Day17 實測) |
再用錄音長度,乘上官方單價估算:
| 辨識方式 | 錄音長度 | 估算 |
|---|---|---|
| 同步辨識 | 5,259 秒(約 88 分鐘) | US$1.40 |
| 串流辨識 | 約 400 秒(約 7 分鐘) | US$0.11 |
| 合計 | 約 95 分鐘 | 約 US$1.5 |
最後對照主控台「帳單」→「報表」,2026 年 9 月、依服務分組,
我的帳單帳戶是新台幣:

| 服務 | 金額(新台幣) |
|---|---|
| Cloud Speech API | 49.73 |
| Cloud Build | 0.63 |
| Cloud Run Functions | 0.00 |
| Cloud Run | 0.00 |
| Cloud Storage、Artifact Registry | 0.00 |
| 合計 | 50.36 |
總共花了新台幣 50.36 元。
今天把同步、串流兩種辨識,都接進了 App:
錄完再問,交給鏢局;邊說邊聽,交給驛站!
Google Cloud Speech to Text 系列,到今天告一段落~
Chirp 3 相較於過往模型,進步很明顯。
不過,也有幾個點,目前沒辦法完全滿足健口動一動的需求:
以個人開發來說,長期營運費用,可能會是一筆不小的負擔。
所以也會評估開源地端的方式:
| 比較 | 雲端(Cloud STT) | 地端(開源模型) |
|---|---|---|
| 費用 | 照錄音長度計費 | 沒有按分鐘計費 |
| 網路 | 需要連線 | 可以離線 |
| 錄音 | 上傳到雲端 | 留在手機上 |
| 辨識品質 | 這兩篇已實測 | 要另外實測 |
| 維護 | 模型由 Google 維護 | 模型大小、手機效能,要自己顧 |
健口動一動App,最後會選擇雲端還是地端,會再多實測、比較後決定。
選型、營運、維護,都沒有標準答案。看清楚需求與限制,找出最適合的做法,也是我的重要課題。
最後,
感謝能實作去年沒做成的雲端串流辨識!
雲端還是地端,這道題先放在心上;
鐵人賽的下一站,回到 Google AI 的主場。
還記得 Day15 收進保險箱的那把 Gemini 金鑰嗎?
那其實是一張聘書:府裡要請一位能看、能寫、還能畫的師爺。
明天就來認識這位師爺:Gemini!
感謝有緣看到這邊的你~
希望佛菩薩也祝福你:🌟平安健康 自在順遂🌟
南無觀世音菩薩🍀 南無地藏菩薩🏠 南無阿彌陀佛☀️